#include "NezhaCFileSystem.h"

#include "NezhaIReadFile.h"
#include "NezhaIWriteFile.h"
#include "NezhaCFileList.h"

#include "NezhaCMemoryReadFile.h"
#include "NezhaCMemoryWriteFile.h"


#include <stdio.h>

#ifdef NZOS_WIN32
#    include <direct.h> // for _chdir
#else
#    include <unistd.h>
#    include <limits.h>
#    include <stdlib.h>
#endif

using namespace Nezha;
using namespace stream;



CFile::CFile(FILE* file, const char* fullName)	
	: mFile(file)
	, mFullPath(fullName)
{
	size_t begin = mFullPath.find_last_of("\\/");
	if(begin != std::string::npos)
	{
		mFileName = &mFullPath.at(begin + 1);
	}
}

CFile::~CFile()
{
	if(mFile)
	{
		fclose(mFile);
	}
}

char CFileSystem::sWorkingDirectory[FILE_SYSTEM_MAX_PATH] = "";

NEZHA_SINGLETON_IMPLEMENT(CFileSystem)

CFileSystem::CFileSystem()
{
    memset(sWorkingDirectory, 0, sizeof(sWorkingDirectory));

	// set as global.
	IFileSystem::SetGlobalFileSystem(static_cast<IFileSystem*>(this));
}

CFileSystem::~CFileSystem()
{
	clear();
}

//! opens a file for read access
IReadFile*
CFileSystem::createReadFile(const char* filename)
{
	IReadFile* file = InternalCreateReadFile(filename);
	
	if(file)
	{
		return file;
	}

	return NULL;
}

/**
IReadFile*
CFileSystem::createMemoryReadFile(void* memory,
								  int len, 
								  const char* fileName,
								  bool deleteMemoryWhenDropped)
{
	if (!memory)
	{
		return 0;
	}
	return NZ_New CMemoryReadFile(memory, len, fileName, deleteMemoryWhenDropped);
}
**/

IWriteFile*
CFileSystem::createWriteFile(const char* filename, bool append)
{
	return InternalCreateWriteFile(filename, append);
}


bool CFileSystem::addZipFileArchive(const char* filename,
										   bool ignoreCase,
										   bool ignorePaths,
										   u32 poolStartSize)
{
	return false;
}

			
bool CFileSystem::addFolderFileArchive(const char* filename,
										      bool ignoreCase,
											  bool ignorePaths)
{
	return false;
}

			
bool CFileSystem::addPakFileArchive(const char* filename,
										   bool ignoreCase,
										   bool ignorePaths)
{
	return false;
}


bool
CFileSystem::removeFileArchive(const char* filename)
{
	return false;
}

const char*
CFileSystem::getWorkingDirectory()
{

#if defined(NZOS_IOS)
	getcwd(sWorkingDirectory, (size_t)FILE_SYSTEM_MAX_PATH);
#endif
	return sWorkingDirectory;
}

bool
CFileSystem::changeWorkingDirectoryTo(const char* newDirectory)
{
	bool success = false;

#if defined(NZOS_WIN32)
	success=(_chdir(newDirectory) == 0);
#else
	success=(chdir(newDirectory) == 0);
#endif

	if (success)
	{
		strcpy(sWorkingDirectory, newDirectory);
	}
	return success;
}

String
CFileSystem::getAbsolutePath(const String& filename) const
{
	const char* p = 0;

#if defined(NZOS_WIN32)
	char fpath[_MAX_PATH];
	p = _fullpath( fpath, filename.c_str(), _MAX_PATH);
#elif defined(NZOS_IOS)
	char fpath[4096];
	p = realpath(filename.c_str(), fpath);
	if(p == NULL)
	{
		p = (char*)filename.c_str();
	}
#else
	p = filename.c_str();
#endif


	return String(p);
}

String
CFileSystem::getFileDir(const String& filename) const
{
	// find last forward or backslash
	int lastSlash = filename.rfind('/');
	const int lastBackSlash = filename.rfind('\\');
	lastSlash = lastSlash > lastBackSlash ? lastSlash : lastBackSlash;

	if(u32(lastSlash) < filename.length())
	{
		return filename.substr(0, lastSlash);
	}
	return ".";
}
/**
String
CFileSystem::getFileBasename(const String& filename, bool keepExtension) const
{
	// find last forward or backslash
	int lastSlash = filename.rfind('/');
	const int lastBackSlash = filename.rfind('\\');
	lastSlash = std::max(lastSlash, lastBackSlash);
	int end = 0;
	if(!keepExtension)
	{
		end = filename.rfind('.');
		if(end == -1)
		{
			end = 0;
		}
		else
		{
			end = filename.size() - end;
		}
	}

	if(u32(lastSlash) < filename.length())
	{
		return filename.substr(lastSlash + 1, filename.size() - lastSlash - 1 - end);
	}
	else if(end != 0)
	{
		return filename.substr(0, filename.size() - end);
	}
	return filename;
}
**/

IFileList*
CFileSystem::createFileList() const
{
	return NZ_New CFileList;
}

bool
CFileSystem::existFile(const char* filename) const
{
	CFile* f = CFileSystem::open(filename, "rb");
	
	if(f) 
	{
		return true;
	}
	return false;
}

bool
CFileSystem::existFile(const String& filename) const
{
	return existFile(filename.c_str());
}

void CFileSystem::clear()
{

}

u32 CFileSystem::releaseUnusedPoolHandles()
{
	return 0;
}

u32 CFileSystem::releaseUnusedPoolHandles(const char* filename)
{
	return 0;
}

CFile* CFileSystem::open(const char*filename, const char *attrib)
{
	String fullpath = filename;
	if(fullpath.find(':') == std::string::npos 
		)
	{
		// only concat working directory + fullpath if working directory has been initialized.
		if(*sWorkingDirectory)
		{
			String wd = sWorkingDirectory;

			// HACK: Add '/' to the working directory if ommited.
			char c = wd[wd.length()-1];
			if(c != '/' && c != '\\')
			{
				wd += '/';
			}

			fullpath = wd + fullpath;
		}
	}

	FILE* file = fopen(fullpath.c_str(), attrib);
	if(file)
	{
		return NZ_New CFile(file, fullpath.c_str());
	}

	return 0;
}


IReadFile* CFileSystem::createAndOpenFileFromArchives(const char* filename)
{
	return NULL;
}

IFileSystem* IFileSystem::msGlobalFileSys = NULL;

IFileSystem* IFileSystem::GetInterface()
{
	return msGlobalFileSys;
}

void IFileSystem::SetGlobalFileSystem(IFileSystem* fs)
{
	msGlobalFileSys = fs;
}
